﻿using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestListGenerator.Common;

namespace TestListGenerator.Tests
{
    /// <summary>
    /// Summary description for VsmdiHelperTests
    /// </summary>
    [TestClass]
    public class VsmdiHelperTests
    {
        public VsmdiHelperTests()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region GenerateVsmdiID
        [TestMethod]
        public void TestGenerateVsmdiID_String_Null()
        {
            try
            {
                VsmdiHelper.GenerateVsmdiID((string)null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException),ex, "An ArgumentNullExcpetion should have been thrown when calling GenerateVsmdiID with a null string");
                return; 
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling GenerateVsmdiID with a null string");
        }
        [TestMethod]
        public void TestGenerateVsmdiID_String_Empty()
        {
            Assert.AreEqual("eea339da-6b5e-0d4b-3255-bfef95601890", VsmdiHelper.GenerateVsmdiID(""), "The GUID was not the expected value when passing in an Empty String");
        }
        [TestMethod]
        public void TestGenerateVsmdiID_String_Valid()
        {
            Assert.AreEqual("dbad0f2e-d345-4000-12a3-4eb058a74177", VsmdiHelper.GenerateVsmdiID("AllYourBaseAreBelongToUs"), "The GUID was not the expected value when passing in AllYourBaseAreBelongToUs");
        }
        [TestMethod]
        public void TestGenerateVsmdiID_Method_Null()
        {
            try
            {
                VsmdiHelper.GenerateVsmdiID((MethodInfo)null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling GenerateVsmdiID with a null MethodInfo");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling GenerateVsmdiID with a null MethodInfo");
        }
        [TestMethod]
        public void TestGenerateVsmdiID_Method_Valid()
        {
            // Get a valid method
            MethodInfo mi = TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "RepeatedCategories");
            Assert.AreEqual("aebae68c-f6ca-a80b-2c43-c4ddd95e4109", VsmdiHelper.GenerateVsmdiID(mi), "Did not get the right ID for method " + TestObjects.TEST_SUBJECT_CLASS_1 + ".RepeatedCategories");
        }
        #endregion

        public const string DEFAULT_TEST_LIST_NAME = @"Lists of Tests";
        public const string DEFAULT_TEST_LIST_ID = "8c43106b-9dc1-4907-a29f-aa66a61bf5b6";
        public const string RUN_CONFIG_NAME = @"Local Test Run";
        public const string RUN_CONFIG_ID = "5b27d636-1ffb-4592-8872-eb34e2d09d24";
        public const string RUN_CONFIG_STORAGE = "localtestrun.testrunconfig";
        public const string RUN_CONFIG_TYPE = "Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common,   PublicKeyToken=b03f5f7f11d50a3a";

        public const string INCLUDED_TEST_LIST_NAME = "Included";
        public const string INCLUDED_TEST_LIST_ID = "f4d29cb1-c6bb-4787-aa0f-3f91ec11a170";
        public const string EXCLUDED_TEST_LIST_NAME = "Excluded";
        public const string EXCLUDED_TEST_LIST_ID = "a9d90125-bd17-4fc9-b97f-38aa981829aa";
        public const string VALID_DLL_PATH = "TestListGenerator.Tests.TestSubject.dll";
        public const string VALID_CONFIG_PATH = "testoutput.vsmdi";

        public void CheckDefaultTestList(TestListsTestList testList)
        {
            // Check it's not null
            Assert.IsNotNull(testList, "The CreateDefaultTestList method returned null!");

            // CHeck the id is set
            Assert.AreEqual(DEFAULT_TEST_LIST_ID, testList.id, "The ID wasn't set correctly for the default list");
            Assert.AreEqual(DEFAULT_TEST_LIST_NAME, testList.name, "The Name wasn't set correctly for the default list");
            Assert.IsNull(testList.parentListId, "The parentID was not set to null for the default list");
            Assert.IsNull(testList.TestLinks, "There were test links created for the default list, which is invalid");

            // Check the run config
            TestListsTestListRunConfiguration runConfig = testList.RunConfiguration;
            Assert.IsNotNull(runConfig, "The run configuration was not set for the default test list");
            Assert.AreEqual(RUN_CONFIG_NAME, runConfig.name, "The run configuration name was not set to the default for the default list");
            Assert.AreEqual(RUN_CONFIG_ID, runConfig.id, "The run configuration id was not set to the default for the default list");
            Assert.AreEqual(RUN_CONFIG_STORAGE, runConfig.storage, "The run configuration storage was not set to the default for the default list");
            Assert.AreEqual(RUN_CONFIG_TYPE, runConfig.type, "The run configuration type was not set to the default for the default list");
        }

        [TestMethod]
        public void TestCreateDefaultTestList()
        {
            TestListsTestList result = VsmdiHelper.CreateDefaultTestList();
            CheckDefaultTestList(result);
        }


        /// <summary>
        /// used to get a sample method list
        /// </summary>
        protected ICollection<MethodInfo> SampleMethodList
        {
            get
            {
                // See if it's been populated yet
                if (_sampleMethodList != null)
                {
                    return _sampleMethodList;
                }

                // Populate it
                _sampleMethodList = new HashSet<MethodInfo>();
                _sampleMethodList.Add(typeof(TestSubject.TestSubject1).GetMethod("OneCategory"));
                _sampleMethodList.Add(typeof(TestSubject.TestSubject1).GetMethod("NoCategories"));
                _sampleMethodList.Add(typeof(TestSubject.TestSubject1).GetMethod("MultipleCategories"));
                return this._sampleMethodList;
            }
        }
        private ICollection<MethodInfo> _sampleMethodList;

        protected Dictionary<string, string> MethodNameMapping = new Dictionary<string, string>
        {
            {"OneCategory", "a676fe2e-4b1c-001a-df88-7b97bc8e18c5"},
            {"NoCategories", "5268f287-177e-2bfb-6d73-f5b901d5bc7c"},
            {"MultipleCategories", "21288216-d8ab-775f-5af3-754293965b02"}
        };

        #region CreateTestLinks
        [TestMethod]
        public void TestCreateTestLinks_NullDllPath()
        {
            try
            {
                VsmdiHelper.CreateTestLinks(null, SampleMethodList);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateTestLinks with a null dll path");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateTestLinks with a null dll path");
            
        }
        [TestMethod]
        public void TestCreateTestLinks_NullTests()
        {
            try
            {
                VsmdiHelper.CreateTestLinks(VALID_DLL_PATH, null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateTestLinks with a null list of tests");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateTestLinks with a null list of tests");

        }
        [TestMethod]
        public void TestCreateTestLinks()
        {
            TestListsTestListTestLink[] links = VsmdiHelper.CreateTestLinks(VALID_DLL_PATH, SampleMethodList);
            CheckTestLinks(links, VALID_DLL_PATH, SampleMethodList);
        }
        #endregion

        #region CreateTestList
        [TestMethod]
        public void TestCreateTestList_NullDllPath()
        {
            try
            {
                VsmdiHelper.CreateTestList(null, INCLUDED_TEST_LIST_NAME, INCLUDED_TEST_LIST_ID, null, SampleMethodList);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null dll path");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null dll path");
        }
        [TestMethod]
        public void TestCreateTestList_NullTestListName()
        {
            try
            {
                VsmdiHelper.CreateTestList(VALID_DLL_PATH, null, INCLUDED_TEST_LIST_ID, null, SampleMethodList);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null test list name");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null test list name");
        }
        [TestMethod]
        public void TestCreateTestList_NullTestListID()
        {
            try
            {
                VsmdiHelper.CreateTestList(VALID_DLL_PATH, INCLUDED_TEST_LIST_NAME, null, null, SampleMethodList);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null test list id");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateTestList with a null test list id");
        }

        public void CheckTestLinks(TestListsTestListTestLink[] testLinks, string dllPath, ICollection<MethodInfo> methodList)
        {
            // Make sure the right number of test links are there
            Assert.AreEqual(methodList.Count, testLinks.Length, "The number of test links created wasn't what we passed in");

            // Check each test link
            foreach (MethodInfo method in methodList)
            {
                // Check it exists int he test links array
                bool foundTestLink = false;
                string methodSignature = method.DeclaringType.FullName + "." + method.Name;
                foreach (TestListsTestListTestLink testLink in testLinks)
                {
                    if (testLink.name == method.Name)
                    {
                        foundTestLink = true;
                        // Make sure the values are correct
                        Assert.AreEqual(MethodNameMapping[method.Name], testLink.id, "The ID wasn't set correctly for " + methodSignature);
                        Assert.AreEqual(dllPath, testLink.storage, "The DLL path wasn't set correctly for " + methodSignature);
                        Assert.AreEqual(VsmdiHelper.UNIT_TEST_ELEMENT_TYPE, testLink.type, "The Type wasn't set correctly for " + methodSignature);
                        break;
                    }
                }
                Assert.IsTrue(foundTestLink, "We were unable to find test link " + methodSignature);
            }
        }

        public void CheckTestList(TestListsTestList testList, string dllPath, string expectedID, string expectedName, string expectedParentID, ICollection<MethodInfo> methodList)
        {
            // check the name and id
            Assert.AreEqual(expectedID, testList.id, "The ID wasn't set correctly for the test list");
            Assert.AreEqual(expectedName, testList.name, "The Name wasn't set correctly for the test list");

            // Make sure the parent ID is set to the default list since we didn't pass one in
            Assert.AreEqual(expectedParentID, testList.parentListId, "The Name wasn't set correctly for the test list");

            // Make sure the run config is null and the testlinks list is not
            Assert.IsNull(testList.RunConfiguration, "The run configuration was not null, but it should have been");
            Assert.IsNotNull(testList.TestLinks, "There were no test links associated with the test list");

            CheckTestLinks(testList.TestLinks, dllPath, methodList);
        }

        [TestMethod]
        public void TestCreateTestList_Valid()
        {
            // Create the test list
            TestListsTestList result = VsmdiHelper.CreateTestList(VALID_DLL_PATH, INCLUDED_TEST_LIST_NAME, INCLUDED_TEST_LIST_ID, null, SampleMethodList);
            CheckTestList(result, VALID_DLL_PATH, INCLUDED_TEST_LIST_ID, INCLUDED_TEST_LIST_NAME, DEFAULT_TEST_LIST_ID, SampleMethodList);
        }

        [TestMethod]
        public void TestCreateIncludedTestList()
        {
            // Create the list
            TestListsTestList result = VsmdiHelper.CreateTestList(VALID_DLL_PATH, VsmdiHelper.VsmdiStaticMapKey.IncludedTestsTestList, null, SampleMethodList);

            // Check the settings
            CheckTestList(result, VALID_DLL_PATH, INCLUDED_TEST_LIST_ID, INCLUDED_TEST_LIST_NAME, DEFAULT_TEST_LIST_ID, SampleMethodList);
        }

        [TestMethod]
        public void TestCreateExcludedTestList()
        {
            // Create the list
            TestListsTestList result = VsmdiHelper.CreateTestList(VALID_DLL_PATH, VsmdiHelper.VsmdiStaticMapKey.ExcludedTestsTestList, null, SampleMethodList);

            // Check the settings
            CheckTestList(result, VALID_DLL_PATH, EXCLUDED_TEST_LIST_ID, EXCLUDED_TEST_LIST_NAME, DEFAULT_TEST_LIST_ID, SampleMethodList);
        }

        #endregion

        #region CreateVsmdiConfigFile 

        public TestListsTestList[] SampleTestLists
        {
            get
            {
                if (_sampleTestLists != null)
                {
                    return _sampleTestLists;
                }
                _sampleTestLists = new TestListsTestList[] 
                {
                    VsmdiHelper.CreateTestList(VALID_DLL_PATH, VsmdiHelper.VsmdiStaticMapKey.IncludedTestsTestList, null, SampleMethodList),
                    VsmdiHelper.CreateTestList(VALID_DLL_PATH, VsmdiHelper.VsmdiStaticMapKey.ExcludedTestsTestList, null, SampleMethodList)
                };
                return _sampleTestLists;
            }
        }
        private TestListsTestList[] _sampleTestLists;

        [TestMethod]
        public void TestCreateVsmdiConfigFile_NullFile()
        {
            try
            {
                VsmdiHelper.CreateVsmdiConfigFile(null, SampleTestLists);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateVsmdiConfigFile with a null path");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateVsmdiConfigFile with a null path");
        }

        [TestMethod]
        public void TestCreateVsmdiConfigFile_NullTests()
        {
            try
            {
                VsmdiHelper.CreateVsmdiConfigFile(VALID_CONFIG_PATH, null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling CreateVsmdiConfigFile with a null test lists");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling CreateVsmdiConfigFile with a null path");
        }

        [TestMethod]
        public void TestCreateVsdmiConfigFile_EmptyList()
        {
            // Delete any old files
            File.Delete(VALID_CONFIG_PATH);

            // Now write out the file
            VsmdiHelper.CreateVsmdiConfigFile(VALID_CONFIG_PATH, new TestListsTestList[0]);

            // Read in back in
            TestLists configFile = VsmdiHelper.ReadVsmdiConfigFile(VALID_CONFIG_PATH);

            // Check the default list is correct (the only item)
            CheckDefaultTestList(configFile.TestList[0]);
        }

        [TestMethod]
        public void TestCreateVsdmiConfigFile()
        {
            // Delete any old files
            File.Delete(VALID_CONFIG_PATH);

            // Now write out the file
            VsmdiHelper.CreateVsmdiConfigFile(VALID_CONFIG_PATH, SampleTestLists);

            // Read in back in
            TestLists configFile = VsmdiHelper.ReadVsmdiConfigFile(VALID_CONFIG_PATH);

            // Check the default list is correct (the only item)
            CheckDefaultTestList(configFile.TestList[0]);

            // Check the included test list
            CheckTestList(configFile.TestList[1], VALID_DLL_PATH, INCLUDED_TEST_LIST_ID, INCLUDED_TEST_LIST_NAME, DEFAULT_TEST_LIST_ID, SampleMethodList);

            // Check the excluded test list
            CheckTestList(configFile.TestList[2], VALID_DLL_PATH, EXCLUDED_TEST_LIST_ID, EXCLUDED_TEST_LIST_NAME, DEFAULT_TEST_LIST_ID, SampleMethodList);
        }
        #endregion

        #region ReadVsmdiConfigFile 
        [TestMethod]
        public void TestReadVsmdiConfigFile_NullPath()
        {
            try
            {
                VsmdiHelper.ReadVsmdiConfigFile(null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullExcpetion should have been thrown when calling ReadVsmdiConfigFile with a null path");
                return;
            }
            Assert.Fail("An ArgumentNullExcpetion should have been thrown when calling ReadVsmdiConfigFile with a null path");
        }

        [TestMethod]
        public void TestReadVsmdiConfigFile_MissingFile()
        {
            try
            {
                VsmdiHelper.ReadVsmdiConfigFile(@"C:\somecoolpath.asd");
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(FileNotFoundException), ex, "An FileNotFoundException should have been thrown when calling ReadVsmdiConfigFile with a invalid path");
                return;
            }
            Assert.Fail("An FileNotFoundException should have been thrown when calling ReadVsmdiConfigFile with a invalid path");
        }
        
        [TestMethod]
        public void TestReadVsmdiConfigFile_InvalidFile()
        {
            try
            {
                VsmdiHelper.ReadVsmdiConfigFile("TestListGenerator.Tests.TestSubject.dll");
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(InvalidOperationException), ex, "An InvalidOperationException should have been thrown when calling ReadVsmdiConfigFile with a invalid file");
                return;
            }
            Assert.Fail("An InvalidOperationException should have been thrown when calling ReadVsmdiConfigFile with a invalid file");
        }        
        #endregion


    }
}
